🎖️GitЯра🎖️
Node / meshtastic / Meshtastic-Android / files / core / repository / src / commonMain / kotlin / org / meshtastic / core / repository / usecase / SendMessageUseCase.kt
Displaying Raw • Download
core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/usecase/SendMessageUseCase.kt 4f86b91973784e4ec05b9a5c7b2dc396ed4848b8 (4f86b919) Text, 6.85 KB
T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.repository.usecase
Tff7b72import T7ee787co.touchlab.kermit.Logger
Tff7b72import T7ee787org.meshtastic.core.common.util.HomoglyphCharacterStringTransformer
Tff7b72import T7ee787org.meshtastic.core.common.util.nowMillis
Tff7b72import T7ee787org.meshtastic.core.model.Capabilities
Tff7b72import T7ee787org.meshtastic.core.model.ContactKey
Tff7b72import T7ee787org.meshtastic.core.model.DataPacket
Tff7b72import T7ee787org.meshtastic.core.model.MessageStatus
Tff7b72import T7ee787org.meshtastic.core.model.Node
Tff7b72import T7ee787org.meshtastic.core.model.NodeAddress
Tff7b72import T7ee787org.meshtastic.core.repository.HomoglyphPrefs
Tff7b72import T7ee787org.meshtastic.core.repository.MessageQueue
Tff7b72import T7ee787org.meshtastic.core.repository.NodeRepository
Tff7b72import T7ee787org.meshtastic.core.repository.PacketRepository
Tff7b72import T7ee787org.meshtastic.core.repository.PlatformAnalytics
Tff7b72import T7ee787org.meshtastic.core.repository.RadioController
Tff7b72import T7ee787org.meshtastic.proto.Config
Tff7b72import T7ee787kotlin.random.Random
T8b949e/**
* Use case for sending a message over the mesh network.
*
* This component orchestrates the process of:
* 1. Resolving the destination and sender information.
* 2. Handling implicit actions for direct messages (e.g., sharing contacts, favoriting).
* 3. Applying message transformations (e.g., homoglyph encoding).
* 4. Persisting the outgoing message in the local history.
* 5. Enqueuing the message for durable delivery via the platform's message queue.
*
* This implementation is platform-agnostic and relies on injected repositories and controllers.
*/
Tff7b72interface T56d364SendMessageUseCase Tb4b4b4{
Tff7b72suspend Tff7b72operator Tff7b72fun Td2a8ffinvokeTb4b4b4(
Te6edf3textTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3contactKeyTb4b4b4: Tffa657String Tff7b72= Ta5d6ff"Ta5d6ff0Tffd700${Te6edf3NodeAddressTb4b4b4.Te6edf3ID_BROADCASTTffd700}Ta5d6ff"Tb4b4b4,
Te6edf3replyIdTb4b4b4: Tffa657Int? Tff7b72= Tff7b72nullTb4b4b4,
Tb4b4b4)Tb4b4b4: Tffa657Int
Tb4b4b4}
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooGenericExceptionCaughtTa5d6ff"Tb4b4b4)
Tff7b72class T56d364SendMessageUseCaseImplTb4b4b4(
Tff7b72private Tff7b72val Te6edf3nodeRepositoryTb4b4b4: Te6edf3NodeRepositoryTb4b4b4,
Tff7b72private Tff7b72val Te6edf3packetRepositoryTb4b4b4: Te6edf3PacketRepositoryTb4b4b4,
Tff7b72private Tff7b72val Te6edf3radioControllerTb4b4b4: Te6edf3RadioControllerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3homoglyphEncodingPrefsTb4b4b4: Te6edf3HomoglyphPrefsTb4b4b4,
Tff7b72private Tff7b72val Te6edf3messageQueueTb4b4b4: Te6edf3MessageQueueTb4b4b4,
Tff7b72private Tff7b72val Te6edf3analyticsTb4b4b4: Te6edf3PlatformAnalyticsTb4b4b4,
Tb4b4b4) Tb4b4b4: Te6edf3SendMessageUseCase Tb4b4b4{
T8b949e/**
* Executes the send message workflow.
*
* @param text The plain text message to send.
* @param contactKey The identifier of the target contact or channel (e.g., "0!ffffffff" for broadcast).
* @param replyId Optional ID of a message being replied to.
*/
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffNestedBlockDepthTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffLongMethodTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffCyclomaticComplexMethodTa5d6ff"Tb4b4b4)
Tff7b72override Tff7b72suspend Tff7b72operator Tff7b72fun Td2a8ffinvokeTb4b4b4(Te6edf3textTb4b4b4: Tffa657StringTb4b4b4, Te6edf3contactKeyTb4b4b4: Tffa657StringTb4b4b4, Te6edf3replyIdTb4b4b4: Tffa657Int?Tb4b4b4)Tb4b4b4: Tffa657Int Tb4b4b4{
Tff7b72val Te6edf3parsedKey Tff7b72= Te6edf3ContactKeyTb4b4b4(Te6edf3contactKeyTb4b4b4)
Tff7b72val Te6edf3channel Tff7b72= Te6edf3parsedKeyTb4b4b4.Te6edf3channelOrNull
Tff7b72val Te6edf3dest Tff7b72= Te6edf3parsedKeyTb4b4b4.Te6edf3addressString
Tff7b72val Te6edf3ourNode Tff7b72= Te6edf3nodeRepositoryTb4b4b4.Te6edf3ourNodeInfoTb4b4b4.Te6edf3value
Tff7b72val Te6edf3fromId Tff7b72= Te6edf3ourNodeTff7b72?.Te6edf3userTff7b72?.Te6edf3id Tff7b72?: Te6edf3NodeAddressTb4b4b4.Te6edf3ID_LOCAL
T8b949e// Direct message side-effects: share the contact's public key (PKI) or
T8b949e// favorite the node (legacy) before sending the first message. PKI DMs use
T8b949e// channel == PKC_CHANNEL_INDEX (8); legacy DMs have no channel prefix
T8b949e// (channel == null). Both formats target a specific node.
Tff7b72val Te6edf3isDirectMessage Tff7b72= Te6edf3channel Tff7b72=Tff7b72= Tff7b72null Tff7b72|Tff7b72| Te6edf3channel Tff7b72=Tff7b72= Te6edf3NodeAddressTb4b4b4.Te6edf3PKC_CHANNEL_INDEX
Tff7b72if Tb4b4b4(Te6edf3isDirectMessageTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3destNode Tff7b72= Te6edf3nodeRepositoryTb4b4b4.Te6edf3getNodeTb4b4b4(Te6edf3destTb4b4b4)
Tff7b72val Te6edf3fwVersion Tff7b72= Te6edf3ourNodeTff7b72?.Te6edf3metadataTff7b72?.Te6edf3firmware_version
Tff7b72val Te6edf3isClientBase Tff7b72= Te6edf3ourNodeTff7b72?.Te6edf3userTff7b72?.Te6edf3role Tff7b72=Tff7b72= Te6edf3ConfigTb4b4b4.Te6edf3DeviceConfigTb4b4b4.Te6edf3RoleTb4b4b4.Te6edf3CLIENT_BASE
Tff7b72val Te6edf3capabilities Tff7b72= Te6edf3CapabilitiesTb4b4b4(Te6edf3fwVersionTb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3capabilitiesTb4b4b4.Te6edf3canSendVerifiedContactsTb4b4b4) Tb4b4b4{
T8b949e// Best-effort: inform firmware of the destination's public key
T8b949e// for its NodeDB cache. The MeshPacket itself carries the key
T8b949e// directly, so the message can be encrypted regardless.
Te6edf3sendSharedContactTb4b4b4(Te6edf3destNodeTb4b4b4)
Tb4b4b4} Tff7b72else Tff7b72if Tb4b4b4(Te6edf3channel Tff7b72=Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
T8b949e// Legacy favoriting only applies to old-style DMs without PKI
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3destNodeTb4b4b4.Te6edf3isFavorite Tff7b72&Tff7b72& Tff7b72!Te6edf3isClientBaseTb4b4b4) Tb4b4b4{
Te6edf3favoriteNodeTb4b4b4(Te6edf3destNodeTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
T8b949e// Apply homoglyph encoding
Tff7b72val Te6edf3finalMessageText Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3homoglyphEncodingPrefsTb4b4b4.Te6edf3homoglyphEncodingEnabledTb4b4b4.Te6edf3valueTb4b4b4) Tb4b4b4{
Te6edf3HomoglyphCharacterStringTransformerTb4b4b4.Te6edf3optimizeUtf8StringWithHomoglyphsTb4b4b4(Te6edf3textTb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3text
Tb4b4b4}
Tff7b72val Te6edf3packetId Tff7b72= Te6edf3RandomTb4b4b4.Te6edf3nextIntTb4b4b4(T79c0ff1Tb4b4b4, Tffa657IntTb4b4b4.Te6edf3MAX_VALUETb4b4b4)
Tff7b72val Te6edf3packet Tff7b72=
Te6edf3DataPacketTb4b4b4(Te6edf3destTb4b4b4, Te6edf3channel Tff7b72?: T79c0ff0Tb4b4b4, Te6edf3finalMessageTextTb4b4b4, Te6edf3replyIdTb4b4b4)Tb4b4b4.Te6edf3apply Tb4b4b4{
Te6edf3from Tff7b72= Te6edf3fromId
Te6edf3id Tff7b72= Te6edf3packetId
Te6edf3status Tff7b72= Te6edf3MessageStatusTb4b4b4.Te6edf3QUEUED
Tb4b4b4}
Tff7b72try Tb4b4b4{
T8b949e// Write to the DB to immediately reflect the queued state on the UI
Te6edf3packetRepositoryTb4b4b4.Te6edf3savePacketTb4b4b4(
Te6edf3myNodeNum Tff7b72= Te6edf3ourNodeTff7b72?.Te6edf3num Tff7b72?: T79c0ff0Tb4b4b4,
Te6edf3contactKey Tff7b72= Te6edf3contactKeyTb4b4b4,
Te6edf3packet Tff7b72= Te6edf3packetTb4b4b4,
Te6edf3receivedTime Tff7b72= Te6edf3nowMillisTb4b4b4,
Tb4b4b4)
T8b949e// Enqueue for durable transmission via the platform-specific queue
Te6edf3messageQueueTb4b4b4.Te6edf3enqueueTb4b4b4(Te6edf3packetIdTb4b4b4)
T8b949e// Reported here rather than at transmission: the queue worker can run long after the RUM
T8b949e// session ended, and re-runs the send on retry.
Te6edf3analyticsTb4b4b4.Te6edf3trackActionTb4b4b4(
Ta5d6ff"Ta5d6ffmessage_sendTa5d6ff"Tb4b4b4,
Te6edf3mapOfTb4b4b4(Ta5d6ff"Ta5d6ffnum_bytesTa5d6ff" Te6edf3to Te6edf3finalMessageTextTb4b4b4.Te6edf3lengthTb4b4b4, Ta5d6ff"Ta5d6ffis_replyTa5d6ff" Te6edf3to Tb4b4b4(Te6edf3replyId Tff7b72!Tff7b72= Tff7b72nullTb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3exTb4b4b4: Te6edf3ExceptionTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3eTb4b4b4(Te6edf3exTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffFailed to enqueue message packetTa5d6ff" Tb4b4b4}
Tff7b72throw Te6edf3ex
Tb4b4b4}
Tff7b72return Te6edf3packetId
Tb4b4b4}
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8fffavoriteNodeTb4b4b4(Te6edf3nodeTb4b4b4: Te6edf3NodeTb4b4b4) Tb4b4b4{
Tff7b72try Tb4b4b4{
Te6edf3radioControllerTb4b4b4.Te6edf3setFavoriteTb4b4b4(Te6edf3nodeTb4b4b4.Te6edf3numTb4b4b4, Te6edf3favorite Tff7b72= Tff7b72trueTb4b4b4)
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3exTb4b4b4: Te6edf3ExceptionTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3eTb4b4b4(Te6edf3exTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffFavorite node errorTa5d6ff" Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffsendSharedContactTb4b4b4(Te6edf3nodeTb4b4b4: Te6edf3NodeTb4b4b4) Tb4b4b4{
Tff7b72try Tb4b4b4{
Tff7b72val Te6edf3accepted Tff7b72= Te6edf3radioControllerTb4b4b4.Te6edf3sendSharedContactTb4b4b4(Te6edf3nodeTb4b4b4.Te6edf3numTb4b4b4)
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3acceptedTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{ Ta5d6ff"Ta5d6ffShared contact for node Tffd700${Te6edf3nodeTb4b4b4.Te6edf3numTffd700}Ta5d6ff was not acknowledged by the radioTa5d6ff" Tb4b4b4}
Tb4b4b4}
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3exTb4b4b4: Te6edf3ExceptionTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3eTb4b4b4(Te6edf3exTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffSend shared contact errorTa5d6ff" Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Served by rngit 1.5.0 - Generated in 0.05s